// ==UserScript==
// @name         Get douyin comment
// @namespace    http://tampermonkey.net/
// @version      0.7.6
// @author       henry li
// @description  下载抖音视频&评论
// @icon         https://www.douyin.com/favicon.ico
// @downloadURL  https://gitee.com/lhhl/vmonkey/raw/master/vmonkey.user.js
// @updateURL    https://gitee.com/lhhl/vmonkey/raw/master/vmonkey.meta.js
// @match        https://www.douyin.com/video/*
// @match        https://www.douyin.com/note/*
// @match        https://www.douyin.com/*?modal_id=*
// @require      https://cdn.jsdelivr.net/npm/vue@3.4.27/dist/vue.global.prod.js
// @grant        GM_addStyle
// @grant        GM_download
// @grant        GM_info
// @grant        unsafeWindow
// @grant        window.onurlchange
// ==/UserScript==

(t=>{if(typeof GM_addStyle=="function"){GM_addStyle(t);return}const o=document.createElement("style");o.textContent=t,document.head.append(o)})(" .slot-ctn{display:flex;flex-direction:column;background-color:#ffffff30;border-radius:10px;padding:4px}.btn-group{display:flex;flex-direction:column;justify-content:flex-start}.btn-group>*{margin:2px}.toolbox-enter-active,.toolbox-leave-active{transition:opacity .5s ease}.toolbox-enter-from,.toolbox-leave-to{opacity:0} ");

(function (vue) {
  'use strict';

  const _export_sfc = (sfc, props) => {
    const target = sfc.__vccOpts || sfc;
    for (const [key, val] of props) {
      target[key] = val;
    }
    return target;
  };
  const _sfc_main$a = {
    props: {
      left: { type: Number, default: 0 },
      top: { type: Number, default: 0 }
    },
    data() {
      return {
        isDragging: false,
        dragLeft: this.left,
        dragTop: this.top,
        offsetX: 0,
        offsetY: 0
      };
    },
    watch: {
      left(newV, oldV) {
        this.dragLeft = newV;
      },
      top(newV, oldV) {
        this.dragTop = newV;
      }
    },
    methods: {
      onMouseDown(event) {
        this.isDragging = true;
        this.offsetX = event.clientX - this.dragLeft;
        this.offsetY = event.clientY - this.dragTop;
        document.addEventListener("mousemove", this.onMouseMove);
        document.addEventListener("mouseup", this.onMouseUp);
      },
      onMouseMove(event) {
        if (this.isDragging) {
          this.dragLeft = event.clientX - this.offsetX;
          this.dragTop = event.clientY - this.offsetY;
        }
      },
      onMouseUp() {
        this.isDragging = false;
        document.removeEventListener("mousemove", this.onMouseMove);
        document.removeEventListener("mouseup", this.onMouseUp);
      }
    }
  };
  function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
    return vue.openBlock(), vue.createElementBlock("div", {
      onMousedown: _cache[0] || (_cache[0] = (...args) => $options.onMouseDown && $options.onMouseDown(...args)),
      style: vue.normalizeStyle({ left: $data.dragLeft + "px", top: $data.dragTop + "px", position: "fixed", zIndex: 99999 })
    }, [
      vue.renderSlot(_ctx.$slots, "default")
    ], 36);
  }
  const __unplugin_components_0$1 = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$3]]);
  const _hoisted_1$3 = { class: "btn-group" };
  const _sfc_main$9 = {
    __name: "ToolsContainer",
    setup(__props) {
      const initLeft = vue.ref(0);
      const slotContent = vue.ref();
      vue.onMounted(() => {
        initLeft.value = window.innerWidth - slotContent.value.clientWidth - 5;
      });
      const expand = vue.ref(false);
      function doExpand(ex) {
        expand.value = ex;
      }
      return (_ctx, _cache) => {
        const _component_Dragable = __unplugin_components_0$1;
        return vue.openBlock(), vue.createBlock(_component_Dragable, {
          left: initLeft.value,
          top: 80
        }, {
          default: vue.withCtx(() => [
            vue.createElementVNode("div", {
              onMouseleave: _cache[0] || (_cache[0] = ($event) => doExpand(false)),
              ref_key: "slotContent",
              ref: slotContent,
              class: "slot-ctn"
            }, [
              vue.createVNode(vue.Transition, { name: "toolbox" }, {
                default: vue.withCtx(() => [
                  vue.createElementVNode("div", _hoisted_1$3, [
                    vue.renderSlot(_ctx.$slots, "default")
                  ])
                ]),
                _: 3
              })
            ], 544)
          ]),
          _: 3
        }, 8, ["left"]);
      };
    }
  };
  const _sfc_main$8 = {};
  const _hoisted_1$2 = /* @__PURE__ */ vue.createElementVNode("button", null, "尚未开发", -1);
  function _sfc_render$2(_ctx, _cache) {
    const _component_ToolsContainer = _sfc_main$9;
    return vue.openBlock(), vue.createBlock(_component_ToolsContainer, null, {
      default: vue.withCtx(() => [
        _hoisted_1$2
      ]),
      _: 1
    });
  }
  const Demo = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$2]]);
  const _sfc_main$7 = {};
  const _hoisted_1$1 = {
    class: "semi-button semi-button-primary semi-button-size-small",
    type: "button",
    "aria-disabled": "false"
  };
  const _hoisted_2 = {
    class: "semi-button-content",
    "x-semi-prop": "children"
  };
  function _sfc_render$1(_ctx, _cache) {
    return vue.openBlock(), vue.createElementBlock("button", _hoisted_1$1, [
      vue.createElementVNode("span", _hoisted_2, [
        vue.renderSlot(_ctx.$slots, "default")
      ])
    ]);
  }
  const __unplugin_components_0 = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["render", _sfc_render$1]]);
  var _GM_download = /* @__PURE__ */ (() => typeof GM_download != "undefined" ? GM_download : void 0)();
  var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)();
  var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)();
  function copyToClipboard(text) {
    try {
      const textarea = document.createElement("textarea");
      textarea.setAttribute("readonly", "readonly");
      textarea.value = text;
      document.body.appendChild(textarea);
      textarea.select();
      let flag = document.execCommand("copy");
      document.body.removeChild(textarea);
      return flag;
    } catch (e) {
      console.log(e);
      return false;
    }
  }
  function saveUrl(url, name) {
    const a = document.createElement("a");
    a.href = url;
    a.download = name;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
  }
  function saveText(txt, filename) {
    const blob = new Blob([txt], { type: "text/plain" });
    const url = URL.createObjectURL(blob);
    saveUrl(url, filename.replace(/[\/:*?"<>|]/g, ""));
    URL.revokeObjectURL(url);
  }
  function getFileNameFromUrl() {
    const href = window.location.href;
    return href.substring(href.lastIndexOf("/") + 1);
  }
  function notify(msg) {
    alert(msg);
  }
  const _sfc_main$6 = {
    __name: "Video",
    setup(__props) {
      function getVideoLink() {
        const list = [];
        document.querySelectorAll("video>source").forEach((v) => list.push(v.src));
        return list[0];
      }
      function popNoVideo() {
        notify("找不到视频链接");
      }
      function openVideoLink() {
        const url = getVideoLink();
        if (url) {
          window.open(url);
        } else {
          popNoVideo();
        }
      }
      const progress = vue.ref(0);
      function downloadVideo() {
        const url = getVideoLink();
        if (!url) {
          popNoVideo();
        } else {
          const name = getFileNameFromUrl() + ".mp4";
          _GM_download({
            url,
            name,
            saveAs: true,
            onprogress: (p) => {
              const { loaded, total } = p;
              progress.value = Math.round(100 * loaded / total);
            },
            onload: () => progress.value = 0
          });
        }
      }
      return (_ctx, _cache) => {
        const _component_DouyinButton = __unplugin_components_0;
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.createVNode(_component_DouyinButton, { onClick: openVideoLink }, {
            default: vue.withCtx(() => [
              vue.createTextVNode("打开视频链接")
            ]),
            _: 1
          }),
          vue.createVNode(_component_DouyinButton, { onClick: downloadVideo }, {
            default: vue.withCtx(() => [
              vue.createTextVNode("下载视频" + vue.toDisplayString(progress.value > 0 ? `(${progress.value}%)` : ""), 1)
            ]),
            _: 1
          })
        ], 64);
      };
    }
  };
  const _sfc_main$5 = {
    __name: "Comments",
    setup(__props) {
      function getComments() {
        const parentDivs = document.querySelectorAll('div[data-e2e="comment-item"]');
        const texts = [window.location.href];
        for (let i = 0; i < parentDivs.length; i++) {
          const bigBro = parentDivs[i].querySelector("div.comment-item-info-wrap");
          const spanElement = bigBro.nextSibling;
          if (spanElement && spanElement.textContent) {
            texts.push(spanElement.textContent);
          }
        }
        return texts.length > 1 ? texts.join("#ic#\n") : "";
      }
      function popNoComments() {
        notify("当前URL无法获取评论!");
      }
      function copyComments() {
        const text = getComments();
        if (text) {
          copyToClipboard(text);
        } else {
          popNoComments();
        }
      }
      function downloadComments() {
        const text = getComments();
        if (text) {
          saveText(text, getFileNameFromUrl() + ".txt");
        } else {
          popNoComments();
        }
      }
      return (_ctx, _cache) => {
        const _component_DouyinButton = __unplugin_components_0;
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.createVNode(_component_DouyinButton, { onClick: copyComments }, {
            default: vue.withCtx(() => [
              vue.createTextVNode("复制评论")
            ]),
            _: 1
          }),
          vue.createVNode(_component_DouyinButton, { onClick: downloadComments }, {
            default: vue.withCtx(() => [
              vue.createTextVNode("下载评论")
            ]),
            _: 1
          })
        ], 64);
      };
    }
  };
  const SCROLL_DELAY = 1e3;
  function useAutoScroll(scrollDelay = SCROLL_DELAY) {
    let scrollInterval = void 0;
    function getScrollPosition() {
      return scrollY || pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    }
    function scrollToBottom() {
      scrollTo(0, document.body.scrollHeight);
    }
    function hasReachedBottom() {
      return getScrollPosition() >= document.body.scrollHeight - innerHeight;
    }
    function scrollLoop() {
      if (!hasReachedBottom()) {
        scrollToBottom();
      } else {
        console.log("auto-scroll: reached the bottom of the page!");
        clearInterval(scrollInterval);
      }
    }
    function startScrolling() {
      scrollInterval = setInterval(scrollLoop, scrollDelay);
    }
    function stopScrolling() {
      clearInterval(scrollInterval);
    }
    return { startScrolling, stopScrolling };
  }
  const _sfc_main$4 = {
    __name: "AutoScroll",
    setup(__props) {
      const { startScrolling, stopScrolling } = useAutoScroll();
      return (_ctx, _cache) => {
        const _component_DouyinButton = __unplugin_components_0;
        return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
          vue.createVNode(_component_DouyinButton, { onClick: vue.unref(startScrolling) }, {
            default: vue.withCtx(() => [
              vue.createTextVNode("开启自动下拉")
            ]),
            _: 1
          }, 8, ["onClick"]),
          vue.createVNode(_component_DouyinButton, { onClick: vue.unref(stopScrolling) }, {
            default: vue.withCtx(() => [
              vue.createTextVNode("暂停自动下拉")
            ]),
            _: 1
          }, 8, ["onClick"])
        ], 64);
      };
    }
  };
  const _sfc_main$3 = {
    __name: "index",
    setup(__props) {
      const curUrl = vue.inject("curUrl");
      const hasComment = vue.computed(() => {
        return curUrl.value.indexOf("/video/") > -1 || curUrl.value.indexOf("/note/") > -1;
      });
      const hasVideo = vue.computed(() => {
        return curUrl.value.indexOf("/video/") > -1 || curUrl.value.indexOf("modal_id=") > -1;
      });
      vue.watch(curUrl, (newUrl, oldUrl) => {
        console.log({ newUrl, oldUrl });
      });
      return (_ctx, _cache) => {
        const _component_DouyinAutoScroll = _sfc_main$4;
        const _component_DouyinComments = _sfc_main$5;
        const _component_DouyinVideo = _sfc_main$6;
        const _component_ToolsContainer = _sfc_main$9;
        return vue.openBlock(), vue.createBlock(_component_ToolsContainer, null, {
          default: vue.withCtx(() => [
            vue.createVNode(_component_DouyinAutoScroll),
            hasComment.value ? (vue.openBlock(), vue.createBlock(_component_DouyinComments, { key: 0 })) : vue.createCommentVNode("", true),
            hasVideo.value ? (vue.openBlock(), vue.createBlock(_component_DouyinVideo, { key: 1 })) : vue.createCommentVNode("", true)
          ]),
          _: 1
        });
      };
    }
  };
  const _sfc_main$2 = {};
  const _hoisted_1 = /* @__PURE__ */ vue.createElementVNode("button", null, "尚未开发", -1);
  function _sfc_render(_ctx, _cache) {
    const _component_ToolsContainer = _sfc_main$9;
    return vue.openBlock(), vue.createBlock(_component_ToolsContainer, null, {
      default: vue.withCtx(() => [
        _hoisted_1
      ]),
      _: 1
    });
  }
  const Baidu = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render]]);
  const _sfc_main$1 = {
    __name: "SiteMatcher",
    setup(__props) {
      const href = window.location.href;
      let com = Demo;
      if (href.startsWith("https://www.douyin.com/")) {
        com = _sfc_main$3;
      } else if (href.startsWith("https://www.baidu.com")) {
        com = Baidu;
      }
      return (_ctx, _cache) => {
        return vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(vue.unref(com)));
      };
    }
  };
  function initUrlchangeEvent() {
    const win = typeof _unsafeWindow === "object" ? _unsafeWindow : window;
    if (win[Symbol.for("onurlchange")] === void 0) {
      let url = new URL(location.href);
      const dispatchEvent = () => {
        const event = new CustomEvent("urlchange", {
          detail: { prev: url, curr: new URL(location.href) },
          bubbles: true
        });
        url = event.detail.curr;
        if (typeof window.onurlchange === "function") {
          window.addEventListener("urlchange", window.onurlchange, {
            once: true
          });
        }
        document.dispatchEvent(event);
      };
      history.pushState = /* @__PURE__ */ ((f) => (...args) => {
        const ret = Reflect.apply(f, history, args);
        dispatchEvent();
        return ret;
      })(history.pushState);
      history.replaceState = /* @__PURE__ */ ((f) => (...args) => {
        const ret = Reflect.apply(f, history, args);
        dispatchEvent();
        return ret;
      })(history.replaceState);
      window.addEventListener("popstate", () => {
        dispatchEvent();
      });
      win[Symbol.for("onurlchange")] = true;
    }
  }
  const _sfc_main = {
    __name: "App",
    setup(__props) {
      const show = vue.ref(true);
      const curUrl = vue.ref(window.location.href);
      vue.provide("curUrl", curUrl);
      initUrlchangeEvent();
      const matches = _GM_info.script.matches.map((match) => new RegExp(match.replaceAll("*", ".*")));
      window.addEventListener("urlchange", (e) => {
        e.stopPropagation();
        const { curr } = e.detail;
        curUrl.value = curr.href;
        let matched = null;
        for (const match of matches) {
          matched = curr.href.match(match);
          if (matched) {
            break;
          }
        }
        show.value = !!matched;
      }, true);
      return (_ctx, _cache) => {
        const _component_SiteMatcher = _sfc_main$1;
        return vue.withDirectives((vue.openBlock(), vue.createBlock(_component_SiteMatcher, null, null, 512)), [
          [vue.vShow, show.value]
        ]);
      };
    }
  };
  vue.createApp(_sfc_main).mount(
    (() => {
      const app = document.createElement("div");
      document.body.append(app);
      return app;
    })()
  );

})(Vue);